import sys
import os
import zipfile
import rarfile
from datetime import datetime

from file_utils import normalize_jar_path

# 新增：从项目根目录（main.py所在目录）计算相对路径
# 获取当前文件(zip_rar_handler.py)的绝对路径
current_file_path = os.path.abspath(__file__)
# 获取项目根目录（假设与main.py同级）
project_root = os.path.dirname(current_file_path)
# 构建UnRAR工具的相对路径
# 开发环境
# unrar_tool_path = os.path.join(project_root, 'tools', 'UnRAR.exe')

# 获取 unRar.exe 的路径（兼容打包后环境）
def get_unrar_path():
    if getattr(sys, 'frozen', False):
        # 打包后：获取临时目录中的 tools 路径
        base_path = sys._MEIPASS
    else:
        # 开发环境：使用当前脚本所在目录
        base_path = os.path.dirname(os.path.abspath(__file__))
    
    return os.path.join(base_path, 'tools', 'UnRAR.exe')

# 打包环境
unrar_tool_path = get_unrar_path()

# 配置rarfile使用项目内的UnRAR工具
rarfile.UNRAR_TOOL = unrar_tool_path



## 压缩文件处理类

class ZipRarHandler:
    @staticmethod
    def _decode_zip_filename(info):
        """尽量修复ZIP条目的中文文件名乱码

        规则：
        - 若UTF-8标志位已设置，直接返回info.filename
        - 否则按cp437回退字节，再尝试以常见中文编码解码（gbk/cp936/big5），失败保留原值
        """
        name = info.filename
        try:
            # ZIP规范：flag_bits 第11位表示 UTF-8（0x800）
            is_utf8 = bool(info.flag_bits & 0x800)
        except Exception:
            is_utf8 = False

        if is_utf8:
            return name.replace('\\', '/')

        # 还原原始字节（zipfile按cp437解码到str），再尝试用常见中文编码解码
        try:
            raw_bytes = name.encode('cp437', errors='replace')
            for enc in ('gbk', 'cp936', 'big5', 'utf-8'):
                try:
                    fixed = raw_bytes.decode(enc)
                    return fixed.replace('\\', '/')
                except Exception:
                    continue
        except Exception:
            pass
        return name.replace('\\', '/')
    @staticmethod
    def index_zip_files_by_name(file_path):
        """按文件名索引压缩包中的所有文件，支持ZIP和RAR格式"""
        file_index = {}
        is_rar = file_path.lower().endswith('.rar')
        
        try:
            if is_rar:
                with rarfile.RarFile(file_path, 'r') as rf:
                    for info in rf.infolist():

                        # 判断RAR条目是否为目录
                        # 1. 统一路径分隔符（处理Windows和Linux路径差异）
                        normalized_name = info.filename.replace('\\', '/')
                        # 2. 目录的核心特征：
                        #   - 文件名以路径分隔符结尾（/或\）
                        #   - 文件大小通常为0（目录本身不存储内容）
                        #   - 结合is_dir属性，但不单独依赖它
                        has_trailing_sep = normalized_name.endswith('/')
                        is_zero_size = info.file_size == 0
                        # 综合判断：满足"有路径分隔符结尾+大小为0"，才视为目录
                        is_directory =  has_trailing_sep and is_zero_size

                        if is_directory:
                            continue
                            
                        normalized_path = normalize_jar_path(info.filename)
                        file_name = os.path.basename(normalized_path)
                        
                        # 处理RAR时间戳
                        if isinstance(info.mtime, datetime):
                            timestamp = info.mtime.timestamp()
                        else:
                            dt = datetime.fromtimestamp(info.mtime)
                            timestamp = dt.timestamp()
                            
                        if file_name not in file_index:
                            file_index[file_name] = []
                        file_index[file_name].append({
                            'path': normalized_path,
                            'info': info,
                            'timestamp': timestamp
                        })
            else:
                with zipfile.ZipFile(file_path, 'r') as zf:
                    for info in zf.infolist():
                        # 使用增强的中文名解码
                        fixed_name = ZipRarHandler._decode_zip_filename(info)
                        if fixed_name.endswith('/'):
                            continue
                        normalized_path = normalize_jar_path(fixed_name)
                        file_name = os.path.basename(normalized_path)
                        # 处理ZIP时间戳
                        dt = datetime(*info.date_time)
                        timestamp = dt.timestamp()
                        if file_name not in file_index:
                            file_index[file_name] = []
                        file_index[file_name].append({
                            'path': normalized_path,
                            'info': info,
                            'timestamp': timestamp
                        })
        
        except Exception as e:
            raise Exception(f"解析压缩文件时出错: {str(e)}")
        
        return file_index

    @staticmethod
    def extract_zip_contents(zip_path, dest_dir):
        """解压压缩包到目标目录"""
        try:
            if zip_path.lower().endswith('.rar'):
                if not os.path.exists(unrar_tool_path):
                    raise FileNotFoundError(f"UnRAR工具未找到: {unrar_tool_path}")
                with rarfile.RarFile(zip_path, 'r') as rar_file:
                    rar_file.extractall(dest_dir)
                return "RAR压缩包已解压"
            else:
                # 手动按条目解压，修复中文名
                with zipfile.ZipFile(zip_path, 'r') as zip_file:
                    for info in zip_file.infolist():
                        fixed_name = ZipRarHandler._decode_zip_filename(info)
                        target_path = os.path.join(dest_dir, fixed_name.replace('/', os.sep))
                        if fixed_name.endswith('/') or info.is_dir():
                            os.makedirs(target_path, exist_ok=True)
                            continue
                        os.makedirs(os.path.dirname(target_path), exist_ok=True)
                        with zip_file.open(info, 'r') as src, open(target_path, 'wb') as dst:
                            dst.write(src.read())
                return "ZIP压缩包已解压"
        except Exception as e:
            raise Exception(f"解压压缩包时出错: {str(e)}")